import { EOChild } from "./childrenBuilder"
import { EOParseResult, mve, newLifeModel, onceLife, orRun, ShouldChange } from "./util"

export const parseUtil = {
	bind<T>(me: mve.LifeModel, value: mve.MTValue<T>, fun: (v: T) => void, shouldChange: ShouldChange<T>) {
		if (mve.isGValueOrDelaySet(value)) {
			let old: T
			if (mve.isDelaySet(value)) {
				me.WatchAfter(value as mve.GValue<T>, function (v) {
					if (shouldChange(old, v)) {
						old = v
						value.after(v, fun)
					}
				})
			} else {
				me.WatchAfter(value as mve.GValue<T>, function (v) {
					if (shouldChange(old, v)) {
						old = v
						fun(v)
					}
				})
			}
		} else {
			fun(value)
		}
	},
	bindKV<T>(me: mve.LifeModel, map: { [key: string]: mve.MTValue<T> }, fun: (k: string, v: T) => void, shouldChange: ShouldChange<T>) {
		mb.Object.forEach(map, function (v, k) {
			parseUtil.bind(me, v, function (value) {
				fun(k, value)
			}, shouldChange)
		})
	}
}

export interface ElementResult<JO, EO> {
	(n: JO): EOChild<EO>
	one(me: mve.LifeModel, n: JO): EOParseResult<EO>
	root(fun: (me: mve.LifeModel) => JO): EOParseResult<EO>
}
/**原始的组装*/
export function buildElement<JO, EO>(
	fun: (me: mve.LifeModel, n: JO) => EOParseResult<EO>
) {
	const out: ElementResult<JO, EO> = function (n) {
		return function (parent, me) {
			const out = fun(me, n)
			parent.push(out.element)
			return out.init
		}
	}
	out.one = fun
	out.root = function (cal) {
		const life = newLifeModel()
		const result = fun(life.me, cal(life.me))
		const init = result.init
		result.init = onceLife(function () {
			const destroy = orRun(init)
			return function () {
				life.destroy()
				orRun(destroy)
			}
		}).init
		return result
	}
	return out
}